﻿//    nVLC
//    
//    Author:  Roman Ginzburg
//
//    nVLC is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    nVLC is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU General Public License for more details.
//     
// ========================================================================

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using nVLC.Declarations;
using nVLC.LibVlcWrapper;
using nVLC.Implementation.Utils;

namespace nVLC.Implementation
{
    internal unsafe sealed class AudioRenderer : DisposableBase, IAudioRenderer
    {
        private IntPtr m_hMediaPlayer;
        private VolumeChangedEventHandler m_volumeCB;
        private NewSoundEventHandler m_soundCB;
        private Func<SoundFormat, SoundFormat> m_formatSetupCB;
        private SoundFormat m_format;
        private List<Delegate> m_callbacks = new List<Delegate>();

        private IntPtr m_hSetup;
        private IntPtr m_hVolume;
        private IntPtr m_hSound;

        public AudioRenderer(IntPtr hMediaPlayer)
        {
            m_hMediaPlayer = hMediaPlayer;

            PlayCallbackEventHandler pceh = PlayCallback;
            VolumeCallbackEventHandler vceh = VolumeCallback;
            SetupCallbackEventHandler sceh = SetupCallback;

            m_hSound = Marshal.GetFunctionPointerForDelegate(pceh);
            m_hVolume = Marshal.GetFunctionPointerForDelegate(vceh);
            m_hSetup = Marshal.GetFunctionPointerForDelegate(sceh);

            m_callbacks.Add(pceh);
            m_callbacks.Add(vceh);
            m_callbacks.Add(sceh);
        }

        public void SetCallbacks(VolumeChangedEventHandler volume, NewSoundEventHandler sound)
        {
            m_volumeCB = volume;
            m_soundCB = sound;

            LibVlcMethods.libvlc_audio_set_callbacks(m_hMediaPlayer, m_hSound, m_hVolume, IntPtr.Zero);
        }

        public void SetFormat(SoundFormat format)
        {
            m_format = format;

            LibVlcMethods.libvlc_audio_set_format(m_hMediaPlayer, m_format.Format.ToUtf8(), m_format.Rate, m_format.Channels);
        }

        public void SetFormatCallback(Func<SoundFormat, SoundFormat> formatSetup)
        {
            m_formatSetupCB = formatSetup;

            LibVlcMethods.libvlc_audio_set_format_callbacks(m_hMediaPlayer, m_hSetup, IntPtr.Zero);
        }

        private void PlayCallback(void* data, void* samples, uint count, long pts)
        {
            Sound s = new Sound();
            s.SamplesData = new IntPtr(samples);
            s.SamplesNumber = count;
            s.Pts = pts;

            if (m_soundCB != null)
            {
                m_soundCB(s);
            }
        }

        private void VolumeCallback(void* data, float volume, bool mute)
        {
            if (m_volumeCB != null)
            {
                m_volumeCB(volume, mute);
            }
        }

        private int SetupCallback(void** data, char* format, int* rate, int* channels)
        {
            IntPtr pFormat = new IntPtr(format);
            string formatStr = Marshal.PtrToStringAnsi(pFormat);

            m_format = new SoundFormat(formatStr, *rate, *channels);

            if (m_formatSetupCB != null)
            {
                m_format = m_formatSetupCB(m_format);
            }
            
            Marshal.Copy(m_format.Format.ToUtf8(), 0, pFormat, 4);
            *rate = m_format.Rate;
            *channels = m_format.Channels;

            return m_format.UseCustomAudioRendering == true ? 0 : 1;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                m_volumeCB = null;
                m_formatSetupCB = null;
                m_soundCB = null;
                m_callbacks.Clear();
            }
        }
    }
}
